import { IUtils } from '@/utils/interface/IUtils';
import { customRef, Ref } from 'vue';
import { v4 as uuidv4 } from 'uuid';

class Utils implements IUtils {
  constructor() {}

  public objCompare(
    obj1: Record<string, any> | null | undefined,
    obj2: Record<string, any> | null | undefined
  ): boolean {
    if (
      // 先判断是否为 null 或 undefined
      obj1 === null ||
      obj2 === null ||
      obj1 === undefined ||
      obj2 === undefined
    ) {
      return obj1 === obj2; // 如果都为 undefined 或 null，则相等；否则不等
    }

    // 确保都是对象
    if (typeof obj1 !== 'object' || typeof obj2 !== 'object') {
      return obj1 === obj2;
    }

    const keys1 = Object.keys(obj1);
    const keys2 = Object.keys(obj2);

    if (keys1.length !== keys2.length) return false;

    for (const key of keys1) {
      if (!Object.prototype.hasOwnProperty.call(obj2, key)) return false;
      if (typeof obj1[key] !== typeof obj2[key]) return false;

      if (typeof obj1[key] === 'object' && obj1[key] !== null) {
        if (!this.objCompare(obj1[key], obj2[key])) return false;
      } else {
        if (obj1[key] !== obj2[key]) return false;
      }
    }

    return true;
  }

  /**
   * 去除字符串中的元素标记
   * @param fragment 带元素标记字符串
   */
  public removeTag(fragment: string) {
    return (
      new DOMParser().parseFromString(fragment, 'text/html').body.textContent ||
      ''
    );
  }

  /**
   * 复制文本到剪贴板
   * @param text
   */
  public async copyToClipboard(text: string): Promise<void> {
    if (navigator.clipboard) {
      try {
        await navigator.clipboard.writeText(text);
      } catch (error) {
        console.error('复制到剪贴板失败:', error);
        throw error;
      }
    } else {
      const textarea = document.createElement('textarea');
      textarea.value = text;
      textarea.style.position = 'fixed'; // 避免影响页面布局
      textarea.style.top = '-9999px';
      document.body.appendChild(textarea);
      textarea.select();
      textarea.setSelectionRange(0, text.length); // 兼容移动端

      try {
        document.execCommand('copy');
      } catch (error) {
        console.error('复制到剪贴板失败（旧方法）:', error);
        throw error;
      } finally {
        document.body.removeChild(textarea);
      }
    }
  }

  public getRandomNumber(min: number, max: number): number {
    return Math.floor(Math.random() * (max + 1 - min)) + min;
  }

  public debounce(fun: Function, duration: number = 1000): Function {
    let timerId: any; // 记录上一次计时器Id

    return (...args: any[]): void => {
      if (timerId) {
        clearTimeout(timerId);
      }

      timerId = setTimeout((): void => {
        fun.apply(this, ...args);
      }, duration);
    };
  }

  public throttle(fun: Function, delay: number = 1000) {
    let valid = true;
    return (...args: any[]) => {
      if (!valid) {
        return false;
      }

      valid = false;
      setTimeout(() => {
        fun.apply(this, args);
        valid = true;
      }, delay);
    };
  }

  private cache = new WeakMap(); // 缓存对象
  /**
   * 深拷贝
   * @param obj 目标对象
   * @returns 拷贝对象
   */
  public deepCopy(obj: any) {
    // return JSON.parse(JSON.stringify(obj))
    if (typeof obj !== 'object' || obj === null) return obj;

    // 缓存中是否存在-解决循环克隆的问题
    const cached = this.cache.get(obj);
    if (cached) return cached;

    let objCopy: any = Array.isArray(obj) ? [] : {};

    // 保持原型链一致性
    Object.setPrototypeOf(objCopy, Object.getPrototypeOf(obj));

    // 添加到缓存中
    this.cache.set(obj, objCopy);

    for (const key in obj) {
      // if (obj.hasOwnProperty(key)) {
      if (Object.prototype.hasOwnProperty.call(obj, key)) {
        objCopy[key] = this.deepCopy(obj[key]);
      }
    }

    // if (typeof obj === 'object') {
    //   for (const key in obj) {
    //     if (Object.prototype.hasOwnProperty.call(obj, key)) {
    //       if (typeof obj[key] === 'object') {
    //         objCopy[key] = this.deepCopy(obj[key]);
    //       } else {
    //         objCopy[key] = obj[key];
    //       }
    //     }
    //   }
    // } else {
    //   objCopy = obj;
    // }

    return objCopy;
  }

  public singleton(className: new (...args: any[]) => Object): {
    new (...args: any[]): Object;
  } {
    let ins: Object | null = null;
    const proxy = new Proxy(className, {
      construct(target, args) {
        if (!ins) {
          ins = new className(...args);
        } else {
          console.warn(`class ${className.name} 只有一个实例`);
        }
        return ins;
      },
    });
    className.prototype.constructor = proxy;
    return proxy;
  }

  public debounceRef<T>(value: T, duration: number = 500): Ref<T> {
    let timer: any;
    return customRef((track, trigger) => {
      return {
        get() {
          track();
          return value;
        },
        set(val: T) {
          if (timer) clearTimeout(timer);
          timer = setTimeout(() => {
            trigger();
            value = val;
          }, duration);
        },
      };
    });
  }

  public generateUuid32(): string {
    return uuidv4().replace(/-/g, '');
  }

  public hasStatusChanged(
    oldData: any,
    newData: any,
    statusProperties: string[]
  ): boolean {
    if (oldData === newData) return false;

    // 判断数组类型，穿透Proxy
    const isArrayA =
      Object.prototype.toString.call(oldData) === '[object Array]';
    const isArrayB =
      Object.prototype.toString.call(newData) === '[object Array]';

    if (isArrayA !== isArrayB) return true;

    // 如果类型不同，返回true
    if (typeof oldData !== typeof newData) return true;

    // 如果不是对象类型或为null，直接比较值
    if (typeof oldData !== 'object' || oldData === null || newData === null) {
      return oldData !== newData;
    }

    // 如果都是数组，逐个比较元素
    if (isArrayA && isArrayB) {
      // 数组长度不同则视为变化
      if (oldData.length !== newData.length) return true;

      // 逐个比较数组中的元素
      for (let i = 0; i < oldData.length; i++) {
        // 递归检查每个元素，确保元素比较也只检查指定属性
        if (this.hasStatusChanged(oldData[i], newData[i], statusProperties)) {
          return true;
        }
      }
      // 所有元素都相同
      return false;
    }

    // 需要检查的特定状态属性
    const statusPropertiesArr = [...statusProperties];

    // 检查这四个属性是否有变化
    for (const prop of statusPropertiesArr) {
      // 任何一个属性值不相等，返回true表示有变化
      if (oldData[prop] !== newData[prop]) {
        return true;
      }
    }

    // 都没有变化
    return false;
  }
}

const utils = new Utils();
export default utils;
